using System;
using System.Collections.Generic;
using UnityEngine;

namespace BToolkit
{
    //本组件可配合 RectLayoutGroup 或者 GridLayoutGroup 使用

    [AddComponentMenu("BToolkit/LuckyCardDriver")]
    public class LuckyCardDriver : MonoBehaviour
    {
        public delegate void CellUpdateAction<T>(T cell, int index);
        public delegate void ComplateAction<T>(T cell);
        class Step
        {
            public Component component;
            public int transIndex;
            public float delay;
        }

        public int roundCount = 3;
        public float minDelay = 0.05f;
        public float maxDelay = 0.5f;
        public bool isRunning { get; private set; }

        private int cellCount;
        private List<Step> stepList = new List<Step>();
        private CellUpdateAction<Step> cellUpdateAction;
        private ComplateAction<Step> complateAction;
        private CTask task;

        private float EaseExpoIn(double t, double b, double c, double d)
        {
            if (t == 0)
            {
                return (float)b;
            }
            else
            {
                return (float)(c * Math.Pow(2, 10 * (t / d - 1)) + b);
            }
        }

        #region 对外方法///////////////////////////////////////////////////////////////////////

        /// <summary>
        /// 设置刷新回调
        /// </summary>
        /// <param name="startIndex">起始索引</param>
        /// <param name="stopIndex">停止的索引</param>
        /// <param name="onCellUpdate">回调绑在Cell上的组件和跑到的Cell索引</param>
        /// <param name="ignoreList">排除掉的索引数组，没有可传null</param>
        public void SetCellUpdateCallback<T>(CellUpdateAction<T> onCellUpdate) where T : Component
        {
            this.cellUpdateAction = (Step step, int index) =>
            {
                onCellUpdate?.Invoke(step.component as T, index);
            };
        }

        /// <summary>
        /// 开始旋转
        /// </summary>
        /// <param name="startIndex">起始索引</param>
        /// <param name="stopIndex">停止的索引</param>
        /// <param name="onRun">回调绑在Cell上的组件和跑到的Cell索引</param>
        /// <param name="ignoreList">排除掉的索引数组，没有可传null</param>
        public void StartRun<T>(int startIndex, int stopIndex, ComplateAction<T> onComplate, int[] ignoreList = null) where T : Component
        {
            StartRunCoroutin(startIndex, stopIndex, onComplate, ignoreList);
        }

        private async void StartRunCoroutin<T>(int startIndex, int stopIndex, ComplateAction<T> onComplate, int[] ignoreList = null) where T : Component
        {
            if (isRunning) return;

            task = CTask.Create();

            this.complateAction = (Step step) =>
            {
                onComplate?.Invoke(step.component as T);
            };

            isRunning = true;
            cellCount = transform.childCount;
            int allStepsCount = 0;
            if (startIndex < stopIndex)
            {
                allStepsCount = cellCount * roundCount + (stopIndex - startIndex) + 1;
            }
            else
            {
                allStepsCount = cellCount * roundCount + (cellCount - startIndex + stopIndex) + 1;
            }

            int totalValidSteps = allStepsCount;
            int cellTransIndex = 0;
            for (int i = 0; i < allStepsCount; i++)
            {
                if (IsIgnore(cellTransIndex, ignoreList)) totalValidSteps--;
                cellTransIndex++;
                if (cellTransIndex > cellCount - 1) cellTransIndex = 0;
            }
            stepList.Clear();
            cellTransIndex = startIndex;
            int index = 0;
            for (int i = 0; i < allStepsCount; i++)
            {
                if (!IsIgnore(cellTransIndex, ignoreList))
                {
                    Step step = new Step();
                    step.transIndex = cellTransIndex;
                    step.component = transform.GetChild(step.transIndex).GetComponent<T>();
                    step.delay = EaseExpoIn(index, minDelay, maxDelay - minDelay, totalValidSteps);
                    stepList.Add(step);
                    index++;
                }
                cellTransIndex++;
                if (cellTransIndex > cellCount - 1) cellTransIndex = 0;
            }
            for (int i = 0; i < stepList.Count; i++)
            {
                Step step = stepList[i];
                await task.Wait(step.delay);
                if (!isRunning)
                {
                    task.Dispose();
                    return;
                }
                this.cellUpdateAction?.Invoke(step, step.transIndex);
                bool complate = i == stepList.Count - 1;
                if (complate)
                {
                    isRunning = false;
                    this.complateAction?.Invoke(step);
                }
            }
            task.Dispose();
        }

        public bool IsIgnore(int index, int[] ignoreList)
        {
            if (ignoreList == null) return false;
            for (int i = 0; i < ignoreList.Length; i++)
            {
                if (index == ignoreList[i])
                {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// 不等转完，立即结束并停在特定的角度
        /// </summary>
        public void StopAtAngle()
        {
            isRunning = false;
        }

        #endregion

    }
}